package com.scs.application.utils;

import com.scs.application.core.exception.BusinessException;
import com.scs.application.core.utils.BeanValidators;
import com.scs.application.core.utils.SpringUtils;
import com.scs.application.core.utils.StringUtils;

import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import javax.validation.Validator;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 默认保存处理器类，用于批量保存操作中的数据校验和错误处理
 * 实现了BatchSaveHandler接口，提供数据校验、保存操作及错误处理的方法
 *
 * @param <E> 实体类型，表示要处理的实体对象类型
 */
public class DefaultSaveHandler<E> implements BatchSaveHandler<E> {
    // 验证器对象，用于实体对象的数据校验
    private final Validator validator = SpringUtils.getBean(Validator.class);

    // 成功计数器，用于记录成功保存的实体数量
    private AtomicInteger successCount = new AtomicInteger(0);
    // 失败计数器，用于记录保存失败的实体数量
    private AtomicInteger failureCount = new AtomicInteger(0);

    // 消息队列，用于存储校验错误或保存错误的信息
    private Queue<String> messageList = new ConcurrentLinkedQueue();

    /**
     * 执行数据校验操作
     * 在保存实体对象之前进行数据校验，确保数据的合法性和完整性
     *
     * @param currentIndex 当前处理的实体对象索引，用于错误信息定位
     * @param entity       待保存的实体对象
     */
    @Override
    public void doValidate(int currentIndex, E entity) {
        BeanValidators.validateWithException(validator, entity);
    }

    /**
     * 数据校验错误时的处理方法
     * 当数据校验失败时，增加失败计数，并记录具体的错误信息
     *
     * @param currentIndex 当前处理的实体对象索引，用于错误信息定位
     * @param entity       数据校验失败的实体对象
     * @param e            校验异常信息，提供了校验失败的具体原因
     */
    @Override
    public void onValidateError(int currentIndex, E entity, ValidationException e) {
        failureCount.incrementAndGet();

        if (e instanceof ConstraintViolationException) {
            messageList.add(MessageFormat.format("第 [{0}] 行数据校验错误：{1}", currentIndex + 2, StringUtils.join(BeanValidators.extractMessage(((ConstraintViolationException) e).getConstraintViolations()))));
        }
    }

    /**
     * 执行保存操作
     * 对实体对象进行实际的保存操作，此方法需由子类具体实现
     *
     * @param currentIndex 当前处理的实体对象索引，用于错误信息定位
     * @param entity       待保存的实体对象
     * @throws BusinessException 保存过程中可能抛出的业务异常
     */
    @Override
    public void doSave(int currentIndex, E entity) throws BusinessException {

    }

    /**
     * 保存成功后的处理方法
     * 当实体对象保存成功时，增加成功计数
     *
     * @param currentIndex 当前处理的实体对象索引
     * @param entity       保存成功的实体对象
     */
    @Override
    public void onSaveSuccess(int currentIndex, E entity) {
        successCount.incrementAndGet();
    }

    /**
     * 保存失败时的处理方法
     * 当实体对象保存失败时，增加失败计数，并记录具体的错误信息
     *
     * @param currentIndex 当前处理的实体对象索引
     * @param entity       保存失败的实体对象
     * @param e            保存失败的异常信息，提供了失败的具体原因
     */
    @Override
    public void onSaveError(int currentIndex, E entity, BusinessException e) {
        failureCount.incrementAndGet();
        messageList.add(e.getMessage());
    }

    /**
     * 获取成功保存的实体数量
     *
     * @return 成功保存的实体数量
     */
    public int getSuccessCount() {
        return successCount.intValue();
    }

    /**
     * 获取保存失败的实体数量
     *
     * @return 保存失败的实体数量
     */
    public int getFailureCount() {
        return failureCount.intValue();
    }

    /**
     * 获取保存过程中的错误信息列表
     * 提供不可修改的消息列表，确保消息的完整性和一致性
     *
     * @return 错误信息列表
     */
    public List<String> getMessageList() {
        return Collections.unmodifiableList(messageList.stream().collect(Collectors.toList()));
    }
}

